/***********************************************************************************************//**
 * \file   hr.c
 * \brief  Heart Rate Service
 ***************************************************************************************************
 * <b> (C) Copyright 2015 Silicon Labs, http://www.silabs.com</b>
 ***************************************************************************************************
 * This file is licensed under the Silabs License Agreement. See the file
 * "Silabs_License_Agreement.txt" for details. Before using this software for
 * any purpose, you must agree to the terms of that agreement.
 **************************************************************************************************/

#include CONFIGURATION_HEADER
#include CALLBACKS_HEADER

/* standard library headers */
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>

/* BG stack headers */
#include "bg_types.h"
#include "gatt_db.h"
#ifdef HOST
#include "gecko_bglib.h"
#else /* !HOST */
#include "native_gecko.h"
#endif /* !HOST */
#include "infrastructure.h"

/* application specific headers */
#include "app_hw.h"
#include "event_control.h"
#include "connection.h"

/* Own header*/
#include "../../features/hr/hr.h"

/***********************************************************************************************//**
 * @addtogroup Features
 * @{
 **************************************************************************************************/

/***********************************************************************************************//**
 * @addtogroup hr
 * @{
 **************************************************************************************************/

/***************************************************************************************************
  Local Macros and Definitions
***************************************************************************************************/

/* Mapping of AppBuilder specific macros */

/* Heart Rate Measurement flags */
#define HR_FORMAT_UINT8                     0
#define HR_FORMAT_UINT16                    1
#if SILABS_AF_PLUGIN_HR_HR_VALUE_FORMAT == HR_UINT8
#define HR_FORMAT                           HR_FORMAT_UINT8
#else
#define HR_FORMAT                           HR_FORMAT_UINT16
#endif /* SILABS_AF_PLUGIN_HR_HR_VALUE_FORMAT */

/* Sensor contact not supported */
#define HR_SENSOR_CONTACT_NOT_SUP           0
/* Sensor contact supported, but not detected */
#define HR_SENSOR_CONTACT_SUPPORTED_NOT_DET 0x04
/* Sensor contact supported, but and detected */
#define HR_SENSOR_CONTACT_SUPPORTED_DET     0x06
#if SILABS_AF_PLUGIN_HR_HR_SENSOR_CONTACT == HR_SENS_NO_SUP
#define HR_SENSOR_CONTACT_STATUS            HR_SENSOR_CONTACT_NOT_SUP
#elif SILABS_AF_PLUGIN_HR_HR_SENSOR_CONTACT == HR_SENS_SUP_NOT_DET
#define HR_SENSOR_CONTACT_STATUS            HR_SENSOR_CONTACT_SUPPORTED_NOT_DET
#else
#define HR_SENSOR_CONTACT_STATUS            HR_SENSOR_CONTACT_SUPPORTED_DET
#endif /* SILABS_AF_PLUGIN_HR_HR_SENSOR_CONTACT */

/* Energy Expended field is not present */
#define HR_ENERGY_EXPENDED_NOT_SUP          0
/* Energy Expended field is present. Units: kilo Joules */
#define HR_ENERGY_EXPENDED_SUP              0x08
#ifdef SILABS_AF_PLUGIN_HR_ENERGY_EXP_SUP
#define HR_ENERGY_EXPENDED_STATUS           HR_ENERGY_EXPENDED_SUP
#else
#define HR_ENERGY_EXPENDED_STATUS           HR_ENERGY_EXPENDED_NOT_SUP
#endif
/* RR-Interval values are not present */
#define HR_RR_INTERVAL_NOT_SUP              0
/* One or more RR-Interval values are present. Units: 1/1024 seconds */
#define HR_RR_INTERVAL_SUP                  0x10
#ifdef SILABS_AF_PLUGIN_HR_RR_INT_SUP
#define HR_RR_INTERVAL_STATUS               HR_RR_INTERVAL_SUP
#else
#define HR_RR_INTERVAL_STATUS               HR_RR_INTERVAL_NOT_SUP
#endif /* SILABS_AF_PLUGIN_HR_RR_INT_SUP */

/* Other profile specific macros */

/** Heart Rate measurement period in ms. */
#define HR_IND_TIMEOUT                      1000
/** Indicates currently there is no active connection using this service. */
#define HR_NO_CONNECTION                    0xFF
/** Default maximum payload length for most PDUs. */
#define ATT_DEFAULT_PAYLOAD_LEN             20

/***************************************************************************************************
 Local Type Definitions
 **************************************************************************************************/

/** R-R Interval measurement. */
typedef struct {
  uint16_t rRIntervalBuffer[7];    /**< RR-Interval */
  uint8_t rRIntervalLength;      /**< RR-Interval */
} hrRrInterval_t;

/** Heart Rate measurement structure. */
typedef struct {
  uint16_t hrMeasVal;            /**< Heart Rate Measurement Value */
  uint8_t flags;                 /**< Flags */
  uint16_t energyExpended;       /**< Energy Expended */
  hrRrInterval_t rRInterval;     /**< RR-Interval */
} hrHeartRateMeas_t;

/***************************************************************************************************
 Local Variables
 **************************************************************************************************/

static hrHeartRateMeas_t hrHeartRateMeas; /* Heart Rate Measurement */

/***************************************************************************************************
 Static Function Declarations
 **************************************************************************************************/
static uint8_t hrBuildMeas(uint8_t *pBuf);
static uint8_t hrProcess(uint8_t *buf);

/***************************************************************************************************
 Public Variable Definitions
 **************************************************************************************************/
EmberEventControl hrTimerEvt = { EVENT_INACTIVE, 0 };

/***************************************************************************************************
 Public Function Definitions
 **************************************************************************************************/

void hrInit(void)
{
  /* Set all flags of Heart Rate Measurement characteristic */
  hrHeartRateMeas.flags = (HR_FORMAT | HR_SENSOR_CONTACT_STATUS | HR_ENERGY_EXPENDED_STATUS | HR_RR_INTERVAL_STATUS);

  /* Make sure timer for Heart Rate measurement is stopped */
  eventControlSetInactive(hrTimerEvt);
}

void hrCharStatusChange(uint8_t connection, uint16_t clientConfig)
{
  /* if the new value of CCC is not 0 (either indication or notification enabled)
   * start heart rate measurement */
  if (clientConfig)
  {
    hrMeasure(); /* make an initial measurement */
    emberAfPluginHrNotificationsOnCallback(); /* call callback */
  }
  else
  {
    eventControlSetInactive(hrTimerEvt); /* stop measurements */
  }
}

void hrMeasure(void)
{
  uint8_t hrTempBuffer[ATT_DEFAULT_PAYLOAD_LEN];
  uint8_t length;

  /* check if the connection is still open */
  if (HR_NO_CONNECTION == conGetConnectionId())
  {
    return;
  }

  length = hrProcess(hrTempBuffer);

  /* Send Heart Rate measurement notification */
  gecko_cmd_gatt_server_send_characteristic_notification(conGetConnectionId(), gattdb_hr_measurement, length, hrTempBuffer);

  eventControlSetDelayMS(&hrTimerEvt, HR_IND_TIMEOUT); /* restart timer */
}

#ifdef SILABS_AF_PLUGIN_HR_ENERGY_EXP_SUP
void hrWrite(uint8array *writeValue)
{
  if (hrHeartRateMeas.flags & HR_ENERGY_EXPENDED_SUP)
  {
    switch (writeValue->data[0])
    {
      case 1:
    	/* Clear Energy Expended Control Point Command Handling */
        appHwClearEnergyExpended();
        gecko_cmd_gatt_server_send_user_write_response(conGetConnectionId(),gattdb_hr_controlpoint, 0);
        break;

      default:
    	/* Invalid Command Error Response */
        gecko_cmd_gatt_server_send_user_write_response(conGetConnectionId(),gattdb_hr_controlpoint, 0x80);
        break;
    }
  }
  else
  {
      /* Control Point Not Supported Error*/
	  gecko_cmd_gatt_server_send_user_write_response(conGetConnectionId(),gattdb_hr_controlpoint, 0x80);
  }
}
#endif /* SILABS_AF_PLUGIN_HR_ENERGY_EXP_SUP */

/***************************************************************************************************
 Static Function Definitions
 **************************************************************************************************/

/***********************************************************************************************//**
 *  \brief  Build a heart rate measurement characteristic.
 *  \param[in]  pBuf  Pointer to buffer to hold the built heart rate measurement characteristic.
 *  \return  Length of pBuf in bytes.
 **************************************************************************************************/
static uint8_t hrBuildMeas(uint8_t *pBuf)
{
  uint8_t i;
  uint8_t *p = pBuf;
  uint8_t flags = hrHeartRateMeas.flags;

  /* Feed HR Measurement flags into the buffer */
  UINT8_TO_BITSTREAM(p, flags);

  /* Feed Heart Rate Measurement characteristic's fields into the buffer if corresponding field is
   * present */
  if (flags & HR_FORMAT_UINT16)
  {
    UINT16_TO_BITSTREAM(p, hrHeartRateMeas.hrMeasVal);
  }
  else
  {
    UINT8_TO_BITSTREAM(p, (uint8_t)hrHeartRateMeas.hrMeasVal);
  }

  if (flags & HR_ENERGY_EXPENDED_SUP)
  {
    UINT16_TO_BITSTREAM(p, hrHeartRateMeas.energyExpended);
  }

  /* time stamp field present */
  if (flags &  HR_RR_INTERVAL_SUP)
  {
    for (i = 0; i < hrHeartRateMeas.rRInterval.rRIntervalLength; i++)
    {
      UINT16_TO_BITSTREAM(p,hrHeartRateMeas.rRInterval.rRIntervalBuffer[i]);
    }
  }

  return (uint8_t)(p - pBuf); /* return length of data to be sent */
}

/***********************************************************************************************//**
 *  \brief  This function is called by the application when the periodic measurement timer expires.
 *  \param[in]  buf  Event message.
 *  \return  length of heart rate measurement.
 **************************************************************************************************/
static uint8_t hrProcess(uint8_t *buf)
{
  int8_t len;

  /* Update module global variables needed for Heart Rate measurement based on sensor values */
  hrHeartRateMeas.hrMeasVal = appHwReadHr();

  if (hrHeartRateMeas.flags & HR_ENERGY_EXPENDED_SUP)
  {
    hrHeartRateMeas.energyExpended = appHwReadEnergyExpended();
  }

  if (hrHeartRateMeas.flags &  HR_RR_INTERVAL_SUP)
  {
    appHwReadRRInterval(&hrHeartRateMeas.rRInterval.rRIntervalLength,(uint16*)hrHeartRateMeas.rRInterval.rRIntervalBuffer);
  }

  emberAfPluginHrMeasAvailableCallback(hrHeartRateMeas.hrMeasVal); /* call callback */

  len = hrBuildMeas(buf);  /* Build heart rate measurement characteristic */

  return len;
}

/** @} (end addtogroup hr) */
/** @} (end addtogroup Features) */

